//
//  ExceptionHandler.m
//  ExceptionHandle
//
//  Created by 常彪 on 16/1/8.
//  Copyright © 2016年 0xcb. All rights reserved.
//

#import "TYExceptionHandler.h"
#import <UIKit/UIKit.h>
#include <ifaddrs.h>
#include <arpa/inet.h>
#import <sys/utsname.h> 
#import "TYSystemInfo.h" 
#import "NSString+TYUrl.h"
//hook 函数的钩子

//名字太长，起一个别名
//把已经存在的类型，定义一个新的名字
typedef NSUncaughtExceptionHandler NSUExcHandler;

//记录原有的异常处理函数指针地址
static NSUExcHandler *orig_ExpHandler = NULL;

//实现一个异常处理的函数
void MyExpHandler(NSException *exception) {
     NSLog(@"=================== %@", exception);
     
     //组装崩溃日志，
     NSMutableArray *logStrArr = [NSMutableArray array];
     
     
     //手机信息
     UIDevice *device = [UIDevice currentDevice];
     [logStrArr addObject:[NSString stringWithFormat:@"系统信息：%@",device.systemName]];//系统信息
     [logStrArr addObject:[NSString stringWithFormat:@"系统版本：%@",device.systemVersion]];//系统版本
     [logStrArr addObject:[NSString stringWithFormat:@"设备类型：%@",device.model]];//
     [logStrArr addObject:[NSString stringWithFormat:@"设备名称：%@",device.name]];//设备名称
     [logStrArr addObject:[NSString stringWithFormat:@"手机机型：%@",[TYSystemInfo getCurrentDeviceName]]];//收集类型
     [logStrArr addObject:[NSString stringWithFormat:@"IP地址：%@",[TYSystemInfo getIPAddress]]];//获取IP地址
     //[logStrArr addObject:device.identifierForVendor];
     
     //App版本信息
     NSDictionary *appInfo = [[NSBundle mainBundle] infoDictionary];
     [logStrArr addObject:[NSString stringWithFormat:@"App名称：%@",appInfo[(NSString *)kCFBundleNameKey]]];
     NSString *app_Version = [appInfo objectForKey:@"CFBundleShortVersionString"];
     [logStrArr addObject:[NSString stringWithFormat:@"App版本：%@",app_Version]];
     [logStrArr addObject:[NSString stringWithFormat:@"App版本标识：%@",appInfo[(id)kCFBundleVersionKey]]];
     [logStrArr addObject:[NSString stringWithFormat:@"App标识：%@",appInfo[(id)kCFBundleIdentifierKey]]];
      
    
    //添加自定义信息
    if ([TYExceptionHandler instance].extensionMsg) {
        NSArray * array = [TYExceptionHandler instance].extensionMsg();
        if (array) [logStrArr addObjectsFromArray:array];
    }
    
    if ([TYExceptionHandler instance].viewControllerName) [logStrArr addObject:[NSString stringWithFormat:@"页面信息：%@",[TYExceptionHandler instance].viewControllerName]];
    
    if ([TYExceptionHandler instance].targetName) [logStrArr addObject:[NSString stringWithFormat:@"页面方法：%@",[TYExceptionHandler instance].targetName]];
    
    if ([TYExceptionHandler instance].portName) [logStrArr addObject:[NSString stringWithFormat:@"请求端口：%@",[TYExceptionHandler instance].portName]];
    
    if ([TYExceptionHandler instance].portRequest) [logStrArr addObject:[NSString stringWithFormat:@"请求参数：%@",[TYExceptionHandler instance].portRequest]];
    
    if ([TYExceptionHandler instance].portData) [logStrArr addObject:[NSString stringWithFormat:@"返回数据：%@",[TYExceptionHandler instance].portData]];
     
     //异常信息
     [logStrArr addObject:[NSString stringWithFormat:@"崩溃原因：%@",exception.reason]];
     
     //调用堆栈
     for (id obj in exception.callStackSymbols) {
          [logStrArr addObject:[NSString stringWithFormat:@"调用堆栈: %@", obj]];
     }
    
//     for (NSNumber *addr in exception.callStackReturnAddresses) {
//          [logStrArr addObject:[NSString stringWithFormat:@"调用堆栈: %@", addr]];
//     }
     
     
     //写日志文件到沙盒
     NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/exception.html"];
     NSLog(@"日志地址 ==== %@", path);
     [logStrArr writeToFile:path atomically:YES];
     
     //最后调用一下系统原有的异常处理
     if (orig_ExpHandler != NULL) {
          orig_ExpHandler(exception);
     }
     /* 伪代码，用语言来描述功能的逻辑，只需要表达清楚逻辑，不需要编译
      if (原来的异常处理不为空) {
      把exception参数，交给原来的异常处理来执行
      }
      */
     
     
     //获取错误信息,并存储到本地,以便下次发送给开发者
     NSArray *arr = [exception callStackSymbols];//得到当前调用栈信息
     NSString *reason = [exception reason];//非常重要，就是崩溃的原因
     NSString *name = [exception name];//异常类型
     
     NSString *crashLogInfo = [NSString stringWithFormat:@"exception type : %@ \n crash reason : %@ \n call stack info : %@", name, reason, arr];
     
     [[NSUserDefaults standardUserDefaults]setObject:crashLogInfo forKey:@"crashLogInfo"];
     [[NSUserDefaults standardUserDefaults]synchronize];
     
     
}


@implementation TYExceptionHandler

+ (void)startExceptionHandler {
     static dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{
          //code to be executed once
          //这个block里面的代码之会执行一次
          
          //一般为了不覆盖系统原有的异常处理，会先获取原有的
          orig_ExpHandler = NSGetUncaughtExceptionHandler();
          //设置新的异常处理函数，参数是函数的指针
          NSSetUncaughtExceptionHandler(MyExpHandler);
          
          //找到沙盒里面之前崩溃的日志，上传到服务器，做统计
          //...
          
     });
}


static TYExceptionHandler * _ExceptionHandler = nil;
+ (instancetype)instance{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _ExceptionHandler = [[TYExceptionHandler alloc]init];
    });
    return _ExceptionHandler;
}

- (NSData *)checkErr {
    NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/exception.html"];
    if ([[NSFileManager defaultManager]fileExistsAtPath:path]) {
        NSLog(@"%@",path);
        NSData * errorData = [[NSData alloc]initWithContentsOfFile:path];
        NSString * errorString = [[NSString alloc]initWithData:errorData encoding:NSUTF8StringEncoding];
        NSLog(@"日志信息 ==== %@",errorString);
        //转成html字符串文件
        errorData =  [errorString.tyToHtmlString dataUsingEncoding:NSUTF8StringEncoding];
        [[NSFileManager defaultManager]removeItemAtPath:path error:nil];
        return errorData;
    }
    return nil;
}

#pragma mark ----初始化
+(instancetype)allocWithZone:(struct _NSZone *)zone{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _ExceptionHandler = [super allocWithZone:zone];
    });
    return _ExceptionHandler;
}
- (instancetype)init{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _ExceptionHandler = [super init];
    });
    return _ExceptionHandler;
}
-(id)copy{
    return self;
}

-(id)mutableCopy{
    return self;
}

-(id)copyWithZone:(NSZone *)zone{
    return self;
}

-(id)mutableCopyWithZone:(NSZone *)zone{
    return self;
}

@end













